/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.lib.ddl.impl; import java.sql.*; import java.util.Set; import java.util.HashMap; import java.util.Vector; import java.net.URL; import java.io.InputStream; import java.util.Iterator; import java.text.ParseException; import org.netbeans.lib.ddl.*; import org.netbeans.lib.ddl.impl.*; /** * The factory used for creating instances of Specification class. * SpecificationFactory collects information about available database * description files and is able to specify if system can control * the database (specified by product name or live connection) or not. * It also provides a list of supported databases. Information about databases * reads from file org/netbeans/lib/ddl/DatabaseSpecification.plist. It's possible to replace it * by setting db.specifications.file system property pointing to another one. * * @author Slavek Psenicka */ public class SpecificationFactory implements DatabaseSpecificationFactory, DriverSpecificationFactory { /** Database description file * You should use PListReader to parse it. */ private final String dbFile = "org/netbeans/lib/ddl/resources/dbspec.plist"; /** Driver description file * You should use PListReader to parse it. */ private final String drvFile = "org/netbeans/lib/ddl/resources/driverspec.plist"; /** Array of SpecificationFiles, found (but not read) files * which describes database products. */ private HashMap dbSpecs; /** Array of SpecificationFiles, found (but not read) files * which describes driver products. */ private HashMap drvSpecs; /** Debug information */ private boolean debug = false; /** Constructor. * Reads a bunch of specification files and prepares sfiles array. Files should * be read from default place or from folder specified by system property named * "db.specifications.folder". */ public SpecificationFactory () throws DDLException { String fileDB = System.getProperty("db.specifications.file"); String fileDrv = System.getProperty("driver.specifications.file"); SpecificationParser parser; try { if (fileDB == null) { ClassLoader cl = getClass().getClassLoader(); InputStream stream = cl.getResourceAsStream(dbFile); if (stream == null) throw new Exception("unable to open stream " + dbFile); parser = new SpecificationParser(stream); dbSpecs = parser.getData(); stream.close(); } else { parser = new SpecificationParser(fileDB); dbSpecs = parser.getData(); } } catch (Exception e) { if (fileDB != null) throw new DDLException("unable to read specifications file " + fileDB + ", " + e.getMessage()); else throw new DDLException("unable to read default specifications file, " + e.getMessage()); } try { if (fileDrv == null) { ClassLoader cl = getClass().getClassLoader(); InputStream stream = cl.getResourceAsStream(drvFile); if (stream == null) throw new Exception("unable to open stream " + drvFile); parser = new SpecificationParser(stream); drvSpecs = parser.getData(); stream.close(); } else { parser = new SpecificationParser(fileDrv); drvSpecs = parser.getData(); } } catch (Exception e) { if (fileDrv != null) throw new DDLException("unable to read specifications file " + fileDrv + ", " + e.getMessage()); else throw new DDLException("unable to read default specifications file, " + e.getMessage()); } } /** Returns array of database products supported by system. * It returns string array only, if you need a Specification instance, use * appropriate createSpecification method. */ public Set supportedDatabases() { return dbSpecs.keySet(); } /** Returns true if database (specified by databaseProductName) is * supported by system. Does not throw exception if it doesn't. */ public boolean isDatabaseSupported(String databaseProductName) { return (dbSpecs.containsKey(databaseProductName)); } /** Creates instance of DatabaseSpecification class; a database-specification * class. This object knows about used database and can be used as * factory for db-manipulating commands. It connects to the database * and reads database metadata. Throws DatabaseProductNotFoundException if database * (obtained from database metadata) is not supported. */ public DatabaseSpecification createSpecification(DBConnection dbcon) throws DatabaseProductNotFoundException, DDLException { Connection con = dbcon.createJDBCConnection(); DatabaseSpecification spec = createSpecification(dbcon, con); try { con.close(); } catch (SQLException ex) { throw new DDLException(ex.getMessage()); } return spec; } /** Creates instance of DatabaseSpecification class; a database-specification * class. This object knows about used database and can be used as * factory for db-manipulating commands. It connects to the database * and reads database metadata. Throws DatabaseProductNotFoundException if database * (obtained from database metadata) is not supported. Uses given Connection */ public DatabaseSpecification createSpecification(DBConnection dbcon, Connection jdbccon) throws DatabaseProductNotFoundException, DDLException { String pn = null; try { boolean close = (jdbccon != null ? false : true); Connection con = (jdbccon != null ? jdbccon : dbcon.createJDBCConnection()); DatabaseMetaData dmd = con.getMetaData(); pn = dmd.getDatabaseProductName().trim(); DatabaseSpecification spec = createSpecification(dbcon, pn, con); if (close) con.close(); return spec; } catch (SQLException e) { throw new DDLException("unable to connect to server"); } catch (Exception e) { throw new DatabaseProductNotFoundException(pn, "unable to create specification, "+e.getMessage()); } } /** Creates instance of DatabaseSpecification class; a database-specification * class. This object knows about used database and can be used as * factory for db-manipulating commands. It connects to database and * reads metadata as createSpecification(DBConnection connection), but always * uses specified databaseProductName. This is not recommended technique. */ public DatabaseSpecification createSpecification(DBConnection connection, String databaseProductName, Connection c) throws DatabaseProductNotFoundException { HashMap product = (HashMap) dbSpecs.get(databaseProductName); if (product == null) throw new DatabaseProductNotFoundException(databaseProductName); HashMap specmap = deepUnion(product, (HashMap) dbSpecs.get("GenericDatabaseSystem"), true); specmap.put("connection", connection); DatabaseSpecification spec = new Specification(specmap, c); spec.setSpecificationFactory(this); return spec; } /** Creates instance of DatabaseSpecification class; a database-specification * class. This object knows about used database and can be used as * factory for db-manipulating commands. It connects to database and * reads metadata as createSpecification(DBConnection connection), but always * uses specified databaseProductName. This is not recommended technique. */ public DatabaseSpecification createSpecification(String databaseProductName, Connection c) throws DatabaseProductNotFoundException { HashMap product = (HashMap) dbSpecs.get(databaseProductName); if (product == null) throw new DatabaseProductNotFoundException(databaseProductName); HashMap specmap = deepUnion(product, (HashMap) dbSpecs.get("GenericDatabaseSystem"), true); return new Specification(specmap, c); } public DatabaseSpecification createSpecification(Connection c) throws DatabaseProductNotFoundException, SQLException { return createSpecification(c, c.getMetaData().getDatabaseProductName().trim()); } public DatabaseSpecification createSpecification(Connection c, String databaseProductName) throws DatabaseProductNotFoundException { HashMap product = (HashMap) dbSpecs.get(databaseProductName); if (product == null) throw new DatabaseProductNotFoundException(databaseProductName); HashMap specmap = deepUnion(product, (HashMap) dbSpecs.get("GenericDatabaseSystem"), true); DatabaseSpecification spec = new Specification(specmap, c); spec.setSpecificationFactory(this); return spec; } /** Returns debug-mode flag */ public boolean isDebugMode() { return debug; } /** Sets debug-mode flag */ public void setDebugMode(boolean mode) { debug = mode; } /** Returns array of driver products supported by system. * It returns string array only, if you need a Specification instance, use * appropriate createDriverSpecification method. */ public Set supportedDrivers() { return drvSpecs.keySet(); } /** Returns true if driver (specified by driverName) is * supported by system. Does not throw exception if it doesn't. */ public boolean isDriverSupported(String driverName) { return (drvSpecs.containsKey(driverName)); } /** Creates instance of DriverSpecification class; a driver-specification * class. This object knows about used driver. */ public DriverSpecification createDriverSpecification(String driverName) { HashMap product = (HashMap) drvSpecs.get(driverName); if (product == null) product = (HashMap) drvSpecs.get("DefaultDriver"); HashMap specmap = deepUnion(product, (HashMap) drvSpecs.get("DefaultDriver"), true); DriverSpecification spec = new DriverSpecification(specmap); spec.setDriverSpecificationFactory(this); return spec; } /** Creates deep copy of Map. * All items will be cloned. Used internally in this object. */ private HashMap deepClone(HashMap map) { HashMap newone = (HashMap)map.clone(); Iterator it = newone.keySet().iterator(); while (it.hasNext()) { Object newkey = it.next(); Object deepobj = null, newobj = newone.get(newkey); if (newobj instanceof HashMap) deepobj = deepClone((HashMap)newobj); else if (newobj instanceof String) deepobj = (Object)new String((String)newobj); else if (newobj instanceof Vector) deepobj = ((Vector)newobj).clone(); newone.put(newkey, deepobj); } return newone; } /** Joins base map with additional one. * Copies keys only if not present in base map. Used internally in this object. */ private HashMap deepUnion(HashMap base, HashMap additional, boolean deep) { Iterator it = additional.keySet().iterator(); while (it.hasNext()) { Object addkey = it.next(); Object addobj = additional.get(addkey); //SQL92 types will be not added into databese type list if (addkey.equals("TypeMap")) continue; if (base.containsKey(addkey)) { Object baseobj = base.get(addkey); if (deep && (baseobj instanceof HashMap) && (addobj instanceof HashMap)) { deepUnion((HashMap)baseobj, (HashMap)addobj, deep); } } else { if (addobj instanceof HashMap) addobj = deepClone((HashMap)addobj); else if (addobj instanceof String) addobj = (Object)new String((String)addobj); else if (addobj instanceof Vector) addobj = ((Vector)addobj).clone(); base.put(addkey, addobj); } } return base; } } /* * <<Log>> * 13 Gandalf 1.12 1/25/00 Radko Najman * 12 Gandalf 1.11 1/12/00 Radko Najman deepUnion() method is not * called for TypeMap key * 11 Gandalf 1.10 12/15/99 Radko Najman driverspec.plist * 10 Gandalf 1.9 11/1/99 Radko Najman getDatabaseProductName().trim() * * 9 Gandalf 1.8 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 8 Gandalf 1.7 9/13/99 Slavek Psenicka * 7 Gandalf 1.6 9/10/99 Slavek Psenicka * 6 Gandalf 1.5 6/15/99 Slavek Psenicka adding support for live * connection * 5 Gandalf 1.4 5/14/99 Slavek Psenicka new version * 4 Gandalf 1.3 4/23/99 Slavek Psenicka Chyba v createSpec pri * ConnectAs * 3 Gandalf 1.2 4/23/99 Slavek Psenicka Opravy v souvislosti se * spravnym throwovanim :) CommandNotImplementedException * 2 Gandalf 1.1 4/23/99 Slavek Psenicka new version * 1 Gandalf 1.0 4/6/99 Slavek Psenicka * $ */